package com.bijava.core;

import com.bijava.util.FileHelper;
import com.bijava.util.FreemarkerHelper;
import com.bijava.util.StringHelper;
import freemarker.cache.FileTemplateLoader;
import freemarker.cache.MultiTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static freemarker.template.Configuration.VERSION_2_3_23;

/**
 * Created by leodu
 * Created on 2018/6/5
 */
public class Generator {

    private static Logger logger = LoggerFactory.getLogger(Generator.class);

    private static final String ENCODING = "UTF-8";

    //模版根目录
    private List<File> templateRootDirs;

    //模版输出目录
    private String outRootDir;

    /**
     * @param templateRootDirs 模版根目录，支持多个
     * @param outRootDir 生成的文件的输出目录
     */
    public Generator(List<File> templateRootDirs, String outRootDir) {
        this.templateRootDirs = templateRootDirs;
        this.outRootDir = outRootDir;
    }

    /**
     *
     * @param templateModel 数据库元数据，用户参数等，用于模版注入值
     * @return
     * @throws Exception
     */
    public Generator generateBy(Map templateModel) throws Exception {
        autoReplacePropertiesValue2DirValue(templateModel);
        processTemplateRootDirs(templateModel);
        return this;
    }

    // 自动替换所有value从 com.company 替换为 com/company,并设置key = key+"_dir"后缀
    private Map autoReplacePropertiesValue2DirValue(Map templateModel) {
        Map dirMap = new HashMap();
        for(Object key : templateModel.keySet()) {
            if(templateModel.get(key) == null){
                continue;
            }
            String value = templateModel.get(key).toString();
            if(value.contains(".")){
                String dir_key = key.toString()+"_dir";
//            if(props.entrySet().contains(dir_key)) {
//                continue;
//            }
                String dir_value = value.toString().replace('.', '/');
                dirMap.put(dir_key, dir_value);
            }

        }
        templateModel.putAll(dirMap);
        return templateModel;
    }

    private void processTemplateRootDirs(Map templateModel) throws Exception {
        if(StringHelper.isBlank(outRootDir)) throw new IllegalStateException("'outRootDir' property must be not null.");
        if(templateRootDirs.size() == 0) throw new IllegalStateException("'templateRootDirs' cannot empty");
        for(int i = 0; i < this.templateRootDirs.size(); i++) {
            File templateRootDir = templateRootDirs.get(i);
            List<Exception> exceptions = scanTemplatesAndProcess(templateRootDir,templateModel);
            if(exceptions != null && !exceptions.isEmpty()){
                for (Exception ex : exceptions){
                    logger.error("generator occured a exception",ex);
                }
            }
        }
    }

    private List<Exception> scanTemplatesAndProcess(File templateRootDir, Map templateModel) throws Exception {
        if(templateRootDir == null) throw new IllegalStateException("'templateRootDir' must be not null");
        //加载模版根目录下所有文件夹及文件
        List<File> srcFiles = FileHelper.searchAllNotIgnoreFile(templateRootDir);

        List<Exception> exceptions = new ArrayList();
        for(int i = 0; i < srcFiles.size(); i++) {
            File srcFile = srcFiles.get(i);
            try {
                new TemplateProcessor().executeGenerate(templateRootDir, templateModel,srcFile);
            }catch(Exception e) {
                exceptions.add(e);
            }
        }
        return exceptions;
    }

    /**
     * 模版处理器
     */
    private class TemplateProcessor {

        private void executeGenerate(File templateRootDir,Map templateModel,File srcFile){
            String templateFile = FileHelper.getRelativePath(templateRootDir, srcFile);
            if(GeneratorHelper.isIgnoreTemplateProcess(srcFile, templateFile)) {
                return;
            }
            String outputFilepath = null;
            try {
                outputFilepath = processForOutputFilePath(templateModel,templateFile);

                //生成文件 写入磁盘
                generateNewFile(templateFile,outputFilepath, templateModel);

            }catch(Exception e) {
                throw new RuntimeException("generate oucur error,templateFile is:" + templateFile+" => "+ outputFilepath+" cause:"+e, e);
            }
        }

        /** 处理文件路径的变量变成输出路径 */
        private String processForOutputFilePath(Map templateModel,String templateFileStr) throws IOException {
            Configuration conf = GeneratorHelper.newFreeMarkerConfiguration(templateRootDirs, ENCODING);
            return FreemarkerHelper.processTemplateString(templateFileStr,templateModel,conf);
        }

//        /** 处理模版内容 */
//        private void processTemplateContent(Map templateModel,String templateFile) throws IOException, TemplateException {
//            //XXX 生成模版
//            Template template = GeneratorHelper.newFreeMarkerConfiguration(templateRootDirs, ENCODING).getTemplate(templateFile);
//            template.process(templateModel, IOHelper.NULL_WRITER);
//        }

        private void generateNewFile( String templateFile,String outputFilepath, Map templateModel) throws Exception {
            Template template = GeneratorHelper.newFreeMarkerConfiguration(templateRootDirs, ENCODING).getTemplate(templateFile);
            template.setOutputEncoding(ENCODING);

            File absoluteOutputFilePath = FileHelper.mkdir(outRootDir,outputFilepath);

            FreemarkerHelper.processTemplate(template, templateModel, absoluteOutputFilePath,ENCODING);
        }

    }

    static class GeneratorHelper {
        //不处理目录 隐藏文件 include文件
        public static boolean isIgnoreTemplateProcess(File srcFile, String templateFile) {
            if (srcFile.isDirectory() || srcFile.isHidden())
                return true;
            if (templateFile.trim().equals(""))
                return true;
            if (srcFile.getName().toLowerCase().endsWith(".include")) {
                return true;
            }
            return false;
        }

        public static Configuration newFreeMarkerConfiguration(List<File> templateRootDirs,String defaultEncoding) throws IOException {
            Configuration conf = new Configuration(VERSION_2_3_23);

            FileTemplateLoader[] templateLoaders = new FileTemplateLoader[templateRootDirs.size()];
            for(int i = 0; i < templateRootDirs.size(); i++) {
                templateLoaders[i] = new FileTemplateLoader(templateRootDirs.get(i));
            }
            MultiTemplateLoader multiTemplateLoader = new MultiTemplateLoader(templateLoaders);

            conf.setTemplateLoader(multiTemplateLoader);
            conf.setNumberFormat("###############");
            conf.setBooleanFormat("true,false");
            conf.setDefaultEncoding(defaultEncoding);

            //TODO 导入公共macro

//            List<String> autoIncludes = getParentPaths(templateName,"macro.include");
//            List<String> availableAutoInclude = FreemarkerHelper.getAvailableAutoInclude(conf,autoIncludes);
//            conf.setAutoIncludes(availableAutoInclude);
            return conf;
        }
    }

}